using System.Linq.Dynamic.Core;
using AutoMapper;
using DealerPlatform.Common.EventBusHelper;
using DealerPlatform.Common.RedisModule;
using DealerPlatform.Core;
using DealerPlatform.Core.Consts;
using DealerPlatform.Core.Core;
using DealerPlatform.Core.Repository;
using DealerPlatform.Domain;
using DealerPlatform.Service.ProductApp.Dtos;
using DealerPlatform.Service.ShoppingCartApp.Dtos;
using Microsoft.EntityFrameworkCore;

namespace DealerPlatform.Service.ProductApp;

public partial class ProductService : IProductService
{
    private readonly IRepository<Product> productRepository;
    private readonly IRepository<ProductSale> productSaleRepository;
    private readonly IRepository<ProductSaleAreaDiff> productSaleAreaDiffRepository;
    private readonly IRepository<ProductPhoto> productPhotoRepository;
    private readonly IMapper mapper;
    private readonly DealerPlatformContext context;
    private readonly IRedisWorker redisWorker;

    public ProductService(IRepository<Product> productRepository,
        IRepository<ProductSale> productSaleRepository,
        IRepository<ProductSaleAreaDiff> productSaleAreaDiffRepository,
        IRepository<ProductPhoto> productPhotoRepository,
        IMapper mapper,
        DealerPlatformContext context,
        LocalEventBus<List<ShoppingCartDto>> localEventBus,
        IRedisWorker redisWorker)
    {
        this.productRepository = productRepository;
        this.productSaleRepository = productSaleRepository;
        this.productSaleAreaDiffRepository = productSaleAreaDiffRepository;
        this.productPhotoRepository = productPhotoRepository;
        this.mapper = mapper;
        this.context = context;
        this.redisWorker = redisWorker;
        localEventBus.localEventHandler += LocalEventHandler;
    }

    private async Task LocalEventHandler(List<ShoppingCartDto> dtos)
    {
        var nos = dtos.Select(d => d.ProductNo);
        // var productDtos = await GetProductByProductNos(nos.ToArray());
        var productDtos = await GetProductByProductNosInCache(nos.ToArray());
        dtos.ForEach(dto =>
        {
            var productDto = productDtos.FirstOrDefault(m => m.ProductNo == dto.ProductNo);
            dto.ProductDto = productDto;
        });
    }

    public async Task<IEnumerable<ProductDto>> GetProductDto(
        string searchText,
        string productType,
        string belongTypeName,
        Dictionary<string, string> productProps,
        PageWithSortDto pageWithSortDto)
    {
        pageWithSortDto.Sort ??= "ProductName";

        var bzgg = productProps.ContainsKey("ProductBZGG") ? productProps["ProductBZGG"] : null;
        productProps.TryGetValue("ProductCd", out string cd);
        productProps.TryGetValue("ProductPp", out string pp);
        productProps.TryGetValue("ProductXh", out string xh);
        productProps.TryGetValue("ProductCz", out string cz);
        productProps.TryGetValue("ProductHb", out string hb);
        productProps.TryGetValue("ProductHd", out string hd);
        productProps.TryGetValue("ProductGy", out string gy);
        productProps.TryGetValue("ProductHs", out string hs);
        productProps.TryGetValue("ProductMc", out string mc);
        productProps.TryGetValue("ProductDj", out string dj);
        productProps.TryGetValue("ProductGg", out string gg);
        productProps.TryGetValue("ProductYs", out string ys);

        int skip = (pageWithSortDto.PageIndex - 1) * pageWithSortDto.PageSize;
        var products = productRepository.GetQueryable()
            .Where(m =>
                m.BelongTypeName.ToLower() == belongTypeName.ToLower() &&
                (m.TypeNo == productType || string.IsNullOrWhiteSpace(productType)) &&
                (m.ProductName.Contains(searchText) || string.IsNullOrWhiteSpace(searchText)) &&
                (bzgg == null || m.ProductBzgg == bzgg)
                && (cd == null || m.ProductCd == cd)
                && (pp == null || m.ProductPp == pp)
                && (xh == null || m.ProductXh == xh)
                && (cz == null || m.ProductCz == cz)
                && (hb == null || m.ProductHb == hb)
                && (hd == null || m.ProductHd == hd)
                && (gy == null || m.ProductGy == gy)
                && (hs == null || m.ProductHs == hs)
                && (mc == null || m.ProductMc == mc)
                && (dj == null || m.ProductDj == dj)
                && (gg == null || m.ProductGg == gg)
                && (ys == null || m.ProductYs == ys)
            )
            .OrderBy(pageWithSortDto.Sort)
            .Skip(skip)
            .Take(pageWithSortDto.PageSize);
        var dtos = mapper.Map<List<ProductDto>>(products);
        var productPhotos = await GetProductPhotosByProductNo(products.Select(x => x.ProductNo).ToArray());
        var productSales = await GetProductSalesByProductNo(products.Select(x => x.ProductNo).ToArray());
        dtos.ForEach(p =>
        {
            p.ProductPhoto = productPhotos.FirstOrDefault(m => m.ProductNo == p.ProductNo);
            p.ProductSale = productSales.FirstOrDefault(m => m.ProductNo == p.ProductNo);
        });
        return dtos;
    }

    public async Task<List<BelongTypeDto>> GetBelongTypeDtoAsync()
    {
        var res = await productRepository.GetQueryable().Select(m => new BelongTypeDto()
        {
            SysNo = m.SysNo,
            BelongTypeName = m.BelongTypeName
        }).Distinct().ToListAsync();
        return res;
    }

    public async Task<IEnumerable<ProductTypeDto>> GetProductType(string belongTypeName)
    {
        var productType = await productRepository.GetQueryable()
            .Where(m => m.BelongTypeName == belongTypeName &&
                        !string.IsNullOrWhiteSpace(m.TypeNo) &&
                        !string.IsNullOrWhiteSpace(m.TypeName))
            .Select(m => new ProductTypeDto()
            {
                TypeNo = m.TypeNo,
                TypeName = m.TypeName
            }).Distinct().ToListAsync();
        return productType;
    }

    public async Task<Dictionary<string, IEnumerable<string>>> GetProductProps(string belongTypeName, string? typeNo)
    {
        Dictionary<string, IEnumerable<string>> dicProductType = new();
        var products = await productRepository
            .GetListAsync(m => m.BelongTypeName == belongTypeName &&
                               (m.TypeNo == typeNo ||
                                string.IsNullOrWhiteSpace(typeNo)));
        dicProductType.Add("ProductBzgg|包装规格",
            products.Select(m => m.ProductBzgg)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductCd|产地",
            products.Select(m => m.ProductCd)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductCz|材质",
            products.Select(m => m.ProductCz)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductDj|等级",
            products.Select(m => m.ProductDj)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductGg|规格",
            products.Select(m => m.ProductGg)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductGy|工艺",
            products.Select(m => m.ProductGy)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductHb|环保",
            products.Select(m => m.ProductHb)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductHd|厚度",
            products.Select(m => m.ProductHd)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductHs|花色",
            products.Select(m => m.ProductHs)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductMc|面材",
            products.Select(m => m.ProductMc)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductPp|品牌",
            products.Select(m => m.ProductPp)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductXh|型号",
            products.Select(m => m.ProductXh)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        dicProductType.Add("ProductYs|颜色",
            products.Select(m => m.ProductYs)
                .Distinct()
                .Where(m => !string.IsNullOrWhiteSpace(m))
                .ToList());
        return dicProductType;
    }

    private static object lockObj = new object();

    public async Task<List<ProductDto>> GetProductByProductNosInCache(params string[] postProductNos)
    {
        List<ProductCto> ctos = [];
        foreach (var postProductNo in postProductNos)
        {
            var res = redisWorker
                .GetHashMemory<ProductCto>($"{RedisKeyName.PEODUCT_KEY}:{postProductNo}")
                .FirstOrDefault();
            if (res == null)
            {
                lock (lockObj)
                {
                    res = redisWorker
                        .GetHashMemory<ProductCto>($"{RedisKeyName.PEODUCT_KEY}:{postProductNo}")
                        .FirstOrDefault();
                    if (res == null)
                    {
                        res = mapper.Map<ProductDto, ProductCto>((GetProductByProductNos(postProductNo))
                            .Result
                            .FirstOrDefault());
                        redisWorker.SetHashMemory<ProductCto>($"{RedisKeyName.PEODUCT_KEY}:{postProductNo}", res);
                    }
                }
            }

            ctos.Add(res);
        }

        return mapper.Map<List<ProductCto>, List<ProductDto>>(ctos);
    }

    private async Task<List<ProductDto>> GetProductByProductNos(params string[] postProductNos)
    {
        var productNos = postProductNos.Distinct();
        var products = await productRepository.GetListAsync(m => productNos.Contains(m.ProductNo));
        var productDtos = mapper.Map<List<Product>, List<ProductDto>>(products);
        var productSales = await GetProductSalesByProductNo(productDtos
            .Select(m => m.ProductNo).ToArray());
        var productPhotos = await GetProductPhotosByProductNo(productDtos
            .Select(x => x.ProductNo).ToArray());
        productDtos.ForEach(p =>
        {
            p.ProductSale = productSales.FirstOrDefault(m =>
                m.ProductNo == p.ProductNo);
            p.ProductPhoto = productPhotos.FirstOrDefault(m =>
                m.ProductNo == p.ProductNo);
        });

        return productDtos;
    }
}